<?php
    /**
     * This file is part of the Intelligencer package.
     * For the full copyright and license, please view the LICENSE
     * file that was distributed with this source code.
     * 
     * @author      drapeko <roman.drapeko@gmail.com>
     * @copyright   Roman Drapeko <roman.drapeko@gmail.com>, 2009 - present
     */

    /**
     * Template Intelligencer is used when you can identify possible locations of the file
     * by the parameters such as the class name 
     * 
     * @author drapeko
     */
    class itTemplateIntelligencer extends itIntelligencer implements itCacheableIntelligencer {
        
        protected $templates = array();
        protected $lastAvailableNullPriority = 1000000;
        
        protected $cache = null;
        protected $cacheAggressive = false;
        
        /**
         * Constructor. Accepts the Intelligencer name.
         * @param string $name
         */
        public function __construct($name = null) {
            $this->cache = new itLocalCache();
            parent::__construct($name);
        }
        
        /**
         * Adds new Template to the Intelligencer. You can set the $priority.
         * If the $priority is null the template will be appended to the end
         * 
         * @param itTemplate $template
         * @param integer $priority
         * @return itIntelligencer
         */
        public function addTemplate(itTemplate $template, $priority = null) {
            if (empty($priority)) {
                $priority = $this->lastAvailableNullPriority++;
            }
            
            $this->templates[$priority][] = $template;
            $template->setIntelligencer($this);
            ksort($this->templates);
            return $this;
        }
        
        /**
         * Adds new Template to the Intelligencer. You can set the $priority.
         * If the $priority is null the template will be appended to the end. 
         * Return the template you just added.
         * 
         * @param itTemplate $template
         * @param integer $priority
         * @return itTemplate
         */
        public function addTemplateAndCreate(itTemplate $template, $priority = null) {
            $this->addTemplate($template, $priority);
            return $template;
        }

        /**
         * Ask Intelligencer to load the class according to the template instructions. 
         * This function is widely used by the itManager.
         * @param $className
         * @return boolean
         */
        public function load($className) {
            if ($this->loadFromCache($className)) {
                return true;
            } else {
                if ($this->isAggressiveCache() && $this->hasCache($className)) {
                    return false;
                }
            }
            
            list($namespace, $class) = itUtil::getClassAndNS($className);
            foreach($this->templates as $priority => $templates) {
                foreach($templates as $t) {
                    $pathes = (array)$t->getPath(array($namespace, $class));
                    foreach($pathes as $file) {
                        if ($this->loadFile($className, $file)) {
                            $this->saveCache($className, $file);
                            return true;
                        }
                    }
                }
            }
            
            $this->saveCache($className, null);
            return false;
        }
        
        protected function loadFile($className, $file) {
            if (is_readable($file) && is_file($file)) {
                include_once($file);
                if (class_exists($className, false) || interface_exists($className, false)) {
                    return true;
                }
            }
            return false;
        }
        
        protected function hasCache($className) {
            if ($this->cache != null) {
                $cacheVal = $this->cache->get($this->getCacheKey($className));

                if (!empty($cacheVal) && is_object($cacheVal) 
                    && $cacheVal instanceof itPersistentCacheablePrimitive) {
                    return true;
                }
            }
            
            return false;
        }

        protected function loadFromCache($className) {
            if ($this->cache != null) {
                $cacheVal = $this->cache->get($this->getCacheKey($className));
                if (!empty($cacheVal)) {
                    $file = $cacheVal->getValue();
                    return $this->loadFile($className, $file);
                }
            }
            return false;
        }
        
        protected function saveCache($className, $file) {
            if ($this->cache != null) {
                $this->cache->set($this->getCacheKey($className), new itPersistentCacheablePrimitive($file));
            }
        }
        
        protected function getCacheKey($className) {
            list($namespace, $class) = itUtil::getLowerClassAndNS($className);
            $name = $this->getName();
            if ($name == null) {
                $name = 'NoTemplateIntelligencerName';
            }
            return $name.'_'.$namespace.'_'.$class;
        }

        /**
         * (non-PHPdoc)
         * @see Intelligencer/Intelligencer/itCacheableIntelligencer#setCache($classRulesCache)
         */
        public function setCache(itCache $cache = null) {
            $this->cache = $cache;
            return $this;
        }
        
        /**
         * (non-PHPdoc)
         * @see Intelligencer/Intelligencer/itCacheableIntelligencer#getCache()
         */
        public function getCache() {
            return $this->cache;
        }
        
        /**
         * (non-PHPdoc)
         * @see Intelligencer/Intelligencer/itCacheableIntelligencer#setAggressiveCache($aggressiveCaching)
         */
        public function setAggressiveCache($aggressive = true) {
            $this->cacheAggressive = $aggressive;
            return $this;
        }
        
        /**
         * (non-PHPdoc)
         * @see Intelligencer/Intelligencer/itCacheableIntelligencer#isAggressiveCache()
         */
        public function isAggressiveCache() {
            return $this->cacheAggressive;
        }
    }

?>